package com.sinszm.wechat.util;

import com.sinszm.common.exception.ApiException;
import com.sinszm.common.exception.SystemApiError;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.regex.Pattern;


/**
 * 基本通用工具方法
 *
 * @author chenjianbo
 */
public final class BasicUtils {

    public static final char[] AC = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F', 'G',
            'H', 'I', 'J', 'K', 'L', 'M', 'N',
            'O', 'P', 'Q', 'R', 'S', 'T',
            'U', 'V', 'W', 'X', 'Y', 'Z'
    };
    private static final String REG_NUMBER = "[0-9]+";

    /**
     * 生成6位密码
     */
    public static String getOneTimePassword() {
        return OneTimePwdUtil.generateTOTP(uuid(), System.currentTimeMillis() + "", 6);
    }

    /**
     * 十进制 转 三十六进制
     *
     * @param n 十进制值
     * @return 三十六进制结果
     */
    public static String intToAc(int n) {
        StringBuilder s = new StringBuilder(16);
        String a;
        while (n != 0) {
            s = s.append(AC[n % 36]);
            n = n / 36;
        }
        a = s.reverse().toString();
        return StringUtils.isEmpty(a) ? "0" : a;
    }

    /**
     * UUID规则返回
     *
     * @return 32位唯一字符串
     */
    public static String uuid() {
        return UUID.randomUUID()
                .toString()
                .toUpperCase()
                .replace("-", "");
    }

    /**
     * 格式化日期为年月日
     * <p>
     * 200101
     * </p>
     *
     * @param date 指定日期
     * @return 两位数年份的年月日
     */
    public static String formatDate(Date date) {
        return String.format("%ty%tm%td", date, date, date);
    }

    /**
     * 格式化字符串长度
     *
     * @param str    原始字符串
     * @param length 字符串最小长度
     * @return 格式化字符串
     */
    public static String formatStringLength(String str, int length) {
        return String.format("%" + length + "s", str)
                .replace(" ", "0");
    }

    /**
     * 文件md5计算
     */
    public static String md5File(MultipartFile multipartFile) throws IOException {
        long size = multipartFile.getSize();
        try (FileInputStream in = (FileInputStream) multipartFile.getInputStream()) {
            MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, size);
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(byteBuffer);
            return DigestUtils.md5Hex(md5.digest());
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    /**
     * 根据两个日期时间计算得到天数
     *
     * @param start
     * @param end
     * @return
     */
    public static long countToDays(Date start, Date end) {
        if (start == null || end == null) {
            return 0;
        }
        LocalDateTime startTime = LocalDateTime.ofInstant(start.toInstant(), ZoneId.systemDefault());
        LocalDateTime endTime = LocalDateTime.ofInstant(end.toInstant(), ZoneId.systemDefault());
        return Math.abs(Duration.between(startTime, endTime).toDays());
    }

    /**
     * 格式化费用为字符串
     *
     * @param fee
     * @return
     */
    public static String formatFeeToString(long fee) {
        NumberFormat format = NumberFormat.getCurrencyInstance(Locale.CHINA);
        DecimalFormat decimalFormat = (DecimalFormat) format;
        return decimalFormat.format(Double.valueOf(fee / 100f));
    }

    /**
     * 格式化费用为数字类型
     *
     * @param fee
     * @return
     */
    public static long formatFeeToLong(String fee) {
        BigDecimal bd = new BigDecimal(
                fee.replace("￥", "")
                        .replace("$", "")
                        .replace(",", "")
                        .replace("，", "")
                        .replace(" ", "")
                        .trim());
        return bd.longValue() * 100;
    }

    /**
     * 正则:验证数字
     */
    public static boolean isNumber(String str) {
        return org.springframework.util.StringUtils.hasText(str) && Pattern.matches(REG_NUMBER, str);
    }

    /**
     * 格式化字符串日期
     *
     * @param stringDate 要求日期格式：20200218000000
     * @return Date时间类型
     */
    public synchronized static Date formatStringToDate(String stringDate) {
        if (StringUtils.isEmpty(stringDate)
                || !isNumber(stringDate)
                || stringDate.length() != 14
        ) {
            throw new ApiException("FORMAT-DATE-ERR", "字符串时间格式不符合");
        }
        try {
            SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
            return format.parse(stringDate);
        } catch (ParseException ignored) {
            throw new ApiException(SystemApiError.SYSTEM_ERROR_02);
        }
    }

    /**
     * 用于List通过forEach得到下标和对象
     * <p>
     * list.forEach(BasicUtils.consumerWithIndex((data, index) ->{ }));
     * </p>
     *
     * @param consumer
     * @param <T>
     * @return
     */
    public static <T> Consumer<T> consumerWithIndex(BiConsumer<T, Integer> consumer) {
        class Obj {
            int i;
        }
        Obj obj = new Obj();
        return t -> {
            int index = obj.i++;
            consumer.accept(t, index);
        };
    }

    /**
     * 格式化字符串
     *
     * @param arg
     * @return
     */
    public static String formatString(String arg) {
        return StringUtils.isEmpty(arg) ? "" : arg.trim();
    }

    /**
     * 计算年龄
     */
    public static long age(Date date) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        return dateTime.until(now, ChronoUnit.YEARS);
    }

}
